package org.javia.kaleidoscope;

import android.view.View;
import android.view.MotionEvent;
import android.content.Context;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Bitmap;
import android.graphics.Path;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.Region;
import android.util.Log;
import java.util.Random;
import android.hardware.*;

public class KaleidoscopeView extends View implements SensorEventListener {
    private Paint paint = new Paint();
    private Path path = new Path();
    private Matrix matrix = new Matrix();
    static private final float SQRT3 = (float) Math.sqrt(3);
    private float H, L; // = 69; // L * SQRT3 / 2;
    // private float L = 2 * H / SQRT3;
    private int width, height;
    private Bitmap bmp;
    private Canvas canvas;
    private RectF rectf = new RectF();
    static private final int MAX_NSHAPE = 10;
    Path shapes[] = new Path[MAX_NSHAPE];
    int colors[]  = new int[MAX_NSHAPE];
    Random random = new Random();
    Matrix grid[] = new Matrix[9];

    public void onSizeChanged(int w, int h, int ow, int oh) {
        width = w;
        height = h;
        H = Math.round(width/4.63f);
        L = 2 * H / SQRT3;
        
        for (int i = 0; i < 9; ++i) {
            grid[i].reset();
        }
	grid[1].postRotate(120);
	grid[1].postScale(1, -1);
	grid[1].postTranslate(1.5f*L, H);
	grid[2].postRotate(240);
	grid[2].postTranslate(1.5f*L, H);
	grid[3].postScale(1, -1);
	grid[3].postTranslate(1.5f * L, H);
	grid[4].postRotate(120);
	grid[4].postTranslate(3f * L, 0);
	grid[5].postRotate(240);
	grid[5].postScale(1, -1);
	grid[5].postTranslate(3f * L, 0);

	for (int i = 0; i < 6; ++i) {
	    grid[i].postTranslate(-L/2, 0);
	}
	for (int i = 0; i < 3; ++i) {
	    grid[i+6].set(grid[i]);
	    grid[i].postTranslate(3*L, 0);
	}

        if (bmp != null) {
            bmp.recycle();
        }
        bmp = Bitmap.createBitmap(width, (int) H, Bitmap.Config.RGB_565);
        canvas = new Canvas(bmp);
	genShapes(10);
    }

    public KaleidoscopeView(Context context) {
	super(context);
	for (int i = 0; i < MAX_NSHAPE; ++i) {
	    shapes[i] = new Path();
	}

	paint.setColor(0xffff0000);
	paint.setStyle(Paint.Style.FILL_AND_STROKE);
	paint.setAntiAlias(true);
	paint.setDither(false);

	for (int i = 0; i < 9; ++i) {
	    grid[i] = new Matrix();
	}
    }

    public void onAccuracyChanged(Sensor s, int v) {
	// Log.d("K", "acc " + s + ": " + v);
    }

    float prev[] = new float[3];
    public void onSensorChanged(SensorEvent event) {
        if (canvas == null) {
            return;
        }
	/*
	String str = "";
	for (float v:values) {
	    str += v + "; ";
	}
	Log.d("K", "sensor " + s + ": " + str);
	*/
	float values[] = event.values;
	float max = Math.max(Math.abs(values[0] - prev[0]),
			     Math.max(Math.abs(values[1] - prev[1]),
				      Math.abs(values[2] - prev[2])));
	if (max > 2) {
	    genShapes((int)max / 2);
	    invalidate();
	    System.arraycopy(values, 0, prev, 0, 3);
	}
    }

    private int lastX, lastY;
    public boolean onTouchEvent(MotionEvent e) {
	int action = e.getAction();
	int x = (int)e.getX();
	int y = (int)e.getY();
	if (action == MotionEvent.ACTION_MOVE) {
	    int dx = x - lastX;
	    int dy = y - lastY;	    
	    int dist = dx*dx + dy*dy;
	    if (dist >= 200) {
		genShapes(dist / 200);
		invalidate();
		lastX = x;
		lastY = y;
	    }
	} else if (e.getAction() == MotionEvent.ACTION_DOWN) {
	    genShapes(10);
	    invalidate();
	    lastX = x;
	    lastY = y;
	}
	return true;
    }

    private void genShapes(int nShape) {
	nShape = Math.min(nShape, MAX_NSHAPE);
	for (int i = 0; i < nShape; ++i) {
	    int color = random.nextInt() | 0xff000000;
	    color &= ~(0xff << (random.nextInt(3) * 8));
	    colors[i] = color;
	    int kind = random.nextInt(3);
	    float x = random.nextFloat() * L;
	    float y = random.nextFloat() * L;
	    float xSize = random.nextFloat() * L / 2;
	    float ySize = random.nextFloat() * L / 2;
	    float rotation = random.nextFloat() * 360;
	    rectf.set(x - xSize/2, y - ySize/2, x+xSize/2, y+ySize/2);
	    path = shapes[i];
	    path.rewind();
	    switch (kind) {
	    case 0:
		path.moveTo(x-xSize/2, y+ySize/2);
		path.lineTo(x+xSize/2, y+ySize/2);
		path.lineTo(x, y-ySize/2);
		path.close();
		break;

	    case 1:
		path.addRect(rectf, Path.Direction.CCW);		
		break;

	    case 2:
		path.addOval(rectf, Path.Direction.CCW);
		break;
	    }
	    matrix.setRotate(rotation, x, y);
	    path.transform(matrix);
	    /*
	    Log.d("K", "shape " + kind + ' ' + x + ' ' + y + ' ' 
		  + xSize + ' ' + ySize);
	    */
	}

	for (int i = 0; i < 9; ++i) {	    
	    drawShape(nShape, canvas, grid[i]);
	}
    }

    Path auxPath = new Path();
    Path clip = new Path();
    private void drawShape(int nShape, Canvas canvas, Matrix m) {
	clip.rewind();
	clip.moveTo(0, 0);
	clip.lineTo(L/2, H);
	clip.lineTo(L, 0);
	clip.close();
	clip.transform(m);
	canvas.clipPath(clip, Region.Op.REPLACE);
	for (int i = 0; i < nShape; ++i) {
	    Path path = shapes[i];
	    paint.setColor(colors[i]);
	    path.transform(m, auxPath);
	    canvas.drawPath(auxPath, paint);
	}	
    }

    protected void onDraw(Canvas canvas) {
	for (float y = 0; y < height; y += (H + H)) {
	    canvas.drawBitmap(bmp, 0, y, paint);
	}

	canvas.scale(1, -1);
	for (float y = H + H; y < height + H; y += (H + H)) {
	    canvas.drawBitmap(bmp, 0, -y, paint);
	}
	// postInvalidateDelayed(30);
    }
}
